package com.kxmalls.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kxmalls.common.constant.ShopConstants;
import com.kxmalls.common.core.domain.PageQuery;
import com.kxmalls.common.core.page.TableDataInfo;
import com.kxmalls.common.enums.OrderLogEnum;
import com.kxmalls.common.enums.OrderStatusEnum;
import com.kxmalls.common.exception.ServiceException;
import com.kxmalls.common.utils.OrderUtil;
import com.kxmalls.common.utils.StringUtils;
import com.kxmalls.order.domain.WmStoreOrder;
import com.kxmalls.order.domain.bo.WmStoreOrderBo;
import com.kxmalls.order.domain.bo.WmStoreOrderRecordBo;
import com.kxmalls.order.domain.vo.WmOrderNowOrderRecordVo;
import com.kxmalls.order.domain.vo.WmStoreOrderRecordVo;
import com.kxmalls.order.domain.vo.WmStoreOrderVo;
import com.kxmalls.order.mapper.WmStoreOrderMapper;
import com.kxmalls.order.service.IWmStoreOrderRecordService;
import com.kxmalls.order.service.IWmStoreOrderService;
import com.kxmalls.user.domain.vo.WmUserVo;
import com.kxmalls.user.service.IWmUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单Service业务层处理
 *
 * @author kxmalls
 * @date 2023-02-15
 */
@RequiredArgsConstructor
@Service
public class WmStoreOrderServiceImpl implements IWmStoreOrderService {

    private final WmStoreOrderMapper baseMapper;

    private final IWmUserService wmUserService;

    private final IWmStoreOrderRecordService wmStoreOrderRecordService;

    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询订单
     */
    @Override
    public WmStoreOrderVo queryById(Long id) {
        WmStoreOrderVo wmStoreOrderVo = baseMapper.selectVoById(id);
        if (ObjectUtil.isEmpty(wmStoreOrderVo)) {
            throw new ServiceException("订单详情不存在");
        }
        int orderStatus = OrderUtil.orderStatus(wmStoreOrderVo.getPaid(), wmStoreOrderVo.getStatus(),
            wmStoreOrderVo.getRefundStatus());

        //订单状态
        String orderStatusStr = OrderUtil.orderStatusStr(wmStoreOrderVo.getPaid()
            , wmStoreOrderVo.getStatus(), wmStoreOrderVo.getShippingType()
            , wmStoreOrderVo.getRefundStatus());

        if (orderStatus == 3) {

            String refundTime = DateUtil.formatDateTime(wmStoreOrderVo.getRefundReasonTime());
            orderStatusStr = "<b style='color:#f124c7'>申请退款</b>" +
                "<span>退款原因：" + wmStoreOrderVo.getRefundReasonWap() + "</span>" +
                "<span>备注说明：" + wmStoreOrderVo.getRefundReasonWapExplain() + "</span>" +
                "<span>退款时间：" + refundTime + "</span>";
        }
        wmStoreOrderVo.setStatusName(orderStatusStr);

        wmStoreOrderVo.setFinishStatus(orderStatus);

        String payTypeName = OrderUtil.payTypeName(wmStoreOrderVo.getPayType()
            , wmStoreOrderVo.getPaid());
        wmStoreOrderVo.setPayTypeName(payTypeName);
        //订单类型处理
        //TODO

        //添加订单状态
        WmStoreOrderRecordBo bo = WmStoreOrderRecordBo.builder().oid(wmStoreOrderVo.getId()).build();
        List<WmStoreOrderRecordVo> storeOrderRecordVos = wmStoreOrderRecordService.queryList(bo);
        wmStoreOrderVo.setStoreOrderRecordVoList(storeOrderRecordVos);
        //添加购物车详情

        //添加用户信息
        wmStoreOrderVo.setWmUserVo(wmUserService.selectByUid(wmStoreOrderVo.getUid()));
        if (wmStoreOrderVo.getWmUserVo() == null) {
            wmStoreOrderVo.setWmUserVo(new WmUserVo());
        }
        return wmStoreOrderVo;
    }

    /**
     * 查询订单列表
     */
    @Override
    public TableDataInfo<WmStoreOrderVo> queryPageList(WmStoreOrderBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WmStoreOrder> lqw = buildQueryWrapper(bo);
        Page<WmStoreOrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<WmStoreOrderVo> storeOrderDTOS = new ArrayList<>();
        for (WmStoreOrderVo wmStoreOrderVo : result.getRecords()) {
            this.orderList(storeOrderDTOS, wmStoreOrderVo);
        }
        return TableDataInfo.build(result);
    }

    private void orderList(List<WmStoreOrderVo> wmStoreOrderVos, WmStoreOrderVo wmStoreOrderVo) {

        int orderStatus = OrderUtil.orderStatus(wmStoreOrderVo.getPaid(), wmStoreOrderVo.getStatus(),
            wmStoreOrderVo.getRefundStatus());


        //订单状态
        String orderStatusStr = OrderUtil.orderStatusStr(wmStoreOrderVo.getPaid()
            , wmStoreOrderVo.getStatus(), wmStoreOrderVo.getShippingType()
            , wmStoreOrderVo.getRefundStatus());

        if (orderStatus == 3) {

            String refundTime = DateUtil.formatDateTime(wmStoreOrderVo.getRefundReasonTime());
            orderStatusStr = "<b style='color:#f124c7'>申请退款</b><br/>" +
                "<span>退款原因：" + wmStoreOrderVo.getRefundReasonWap() + "</span><br/>" +
                "<span>备注说明：" + wmStoreOrderVo.getRefundReasonWapExplain() + "</span><br/>" +
                "<span>退款时间：" + refundTime + "</span><br/>";
        }
        wmStoreOrderVo.setStatusName(orderStatusStr);

        wmStoreOrderVo.setFinishStatus(orderStatus);

        String payTypeName = OrderUtil.payTypeName(wmStoreOrderVo.getPayType()
            , wmStoreOrderVo.getPaid());
        wmStoreOrderVo.setPayTypeName(payTypeName);
        //订单类型处理
        //TODO
        //购物车信息跳过
        wmStoreOrderVo.setWmUserVo(wmUserService.selectByUid(wmStoreOrderVo.getUid()));
        if (wmStoreOrderVo.getWmUserVo() == null) {
            wmStoreOrderVo.setWmUserVo(new WmUserVo());
        }
        wmStoreOrderVos.add(wmStoreOrderVo);
    }

    /**
     * 查询订单列表
     */
    @Override
    public List<WmStoreOrderVo> queryList(WmStoreOrderBo bo) {
        LambdaQueryWrapper<WmStoreOrder> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WmStoreOrder> buildQueryWrapper(WmStoreOrderBo bo) {
        Map<String, Object> params = bo.getParams();

        //默认查询所有快递订单
        bo.setShippingType(OrderStatusEnum.SHIPPIING_TYPE_1.getValue());
        //订单状态查询
        if (StrUtil.isNotEmpty(bo.getOrderStatus())) {
            switch (bo.getOrderStatus()) {
                case "0":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_0.getValue());
                    bo.setStatus(OrderStatusEnum.STATUS_0.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_0.getValue());
                    break;
                case "1":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setStatus(OrderStatusEnum.STATUS_0.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_0.getValue());
                    break;
                case "2":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setStatus(OrderStatusEnum.STATUS_1.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_0.getValue());
                    break;
                case "3":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setStatus(OrderStatusEnum.STATUS_2.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_0.getValue());
                    break;
                case "4":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setStatus(OrderStatusEnum.STATUS_3.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_0.getValue());
                    break;
                case "-1":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_1.getValue());
                    break;
                case "-2":
                    bo.setPaid(OrderStatusEnum.PAY_STATUS_1.getValue());
                    bo.setRefundStatus(OrderStatusEnum.REFUND_STATUS_2.getValue());
                    break;
                default:
            }
        }

        //订单类型查询
        if (StrUtil.isNotEmpty(bo.getOrderType())) {
            switch (bo.getOrderType()) {
                case "1":
                    bo.setBargainId(0L);
                    bo.setCombinationId(0L);
                    bo.setSeckillId(0L);
                    break;
                case "2":
                    bo.setCombinationId(0L);
                    break;
                case "3":
                    bo.setSeckillId(0L);
                    break;
                case "4":
                    bo.setBargainId(0L);
                    break;
                case "5":
                    bo.setShippingType(2);
                    break;
                case "6":
                    bo.setPayIntegral(new BigDecimal("0.00"));
                    break;
                default:
            }
        }

        LambdaQueryWrapper<WmStoreOrder> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getOrderId()), WmStoreOrder::getOrderId, bo.getOrderId());
        lqw.eq(StringUtils.isNotBlank(bo.getExtendOrderId()), WmStoreOrder::getExtendOrderId, bo.getExtendOrderId());
        lqw.eq(bo.getUid() != null, WmStoreOrder::getUid, bo.getUid());
        lqw.like(StringUtils.isNotBlank(bo.getRealName()), WmStoreOrder::getRealName, bo.getRealName());
        lqw.likeRight(StringUtils.isNotBlank(bo.getUserPhone()), WmStoreOrder::getUserPhone, bo.getUserPhone());
        lqw.eq(StringUtils.isNotBlank(bo.getUserAddress()), WmStoreOrder::getUserAddress, bo.getUserAddress());
        lqw.eq(StringUtils.isNotBlank(bo.getCartId()), WmStoreOrder::getCartId, bo.getCartId());
        lqw.eq(bo.getFreightPrice() != null, WmStoreOrder::getFreightPrice, bo.getFreightPrice());
        lqw.eq(bo.getTotalNum() != null, WmStoreOrder::getTotalNum, bo.getTotalNum());
        lqw.eq(bo.getTotalPrice() != null, WmStoreOrder::getTotalPrice, bo.getTotalPrice());
        lqw.eq(bo.getTotalPostage() != null, WmStoreOrder::getTotalPostage, bo.getTotalPostage());
        lqw.eq(bo.getPayPrice() != null, WmStoreOrder::getPayPrice, bo.getPayPrice());
        lqw.eq(bo.getPayPostage() != null, WmStoreOrder::getPayPostage, bo.getPayPostage());
        lqw.eq(bo.getDeductionPrice() != null, WmStoreOrder::getDeductionPrice, bo.getDeductionPrice());
        lqw.eq(bo.getCouponId() != null, WmStoreOrder::getCouponId, bo.getCouponId());
        lqw.eq(bo.getCouponPrice() != null, WmStoreOrder::getCouponPrice, bo.getCouponPrice());
        lqw.eq(bo.getPaid() != null, WmStoreOrder::getPaid, bo.getPaid());
        lqw.eq(bo.getPayTime() != null, WmStoreOrder::getPayTime, bo.getPayTime());
        lqw.eq(StringUtils.isNotBlank(bo.getPayType()), WmStoreOrder::getPayType, bo.getPayType());
        lqw.eq(bo.getStatus() != null, WmStoreOrder::getStatus, bo.getStatus());
        lqw.eq(bo.getRefundStatus() != null, WmStoreOrder::getRefundStatus, bo.getRefundStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundReasonWapImg()), WmStoreOrder::getRefundReasonWapImg, bo.getRefundReasonWapImg());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundReasonWapExplain()), WmStoreOrder::getRefundReasonWapExplain, bo.getRefundReasonWapExplain());
        lqw.eq(bo.getRefundReasonTime() != null, WmStoreOrder::getRefundReasonTime, bo.getRefundReasonTime());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundReasonWap()), WmStoreOrder::getRefundReasonWap, bo.getRefundReasonWap());
        lqw.eq(StringUtils.isNotBlank(bo.getRefundReason()), WmStoreOrder::getRefundReason, bo.getRefundReason());
        lqw.eq(bo.getRefundPrice() != null, WmStoreOrder::getRefundPrice, bo.getRefundPrice());
        lqw.eq(StringUtils.isNotBlank(bo.getDeliverySn()), WmStoreOrder::getDeliverySn, bo.getDeliverySn());
        lqw.like(StringUtils.isNotBlank(bo.getDeliveryName()), WmStoreOrder::getDeliveryName, bo.getDeliveryName());
        lqw.eq(StringUtils.isNotBlank(bo.getDeliveryType()), WmStoreOrder::getDeliveryType, bo.getDeliveryType());
        lqw.eq(StringUtils.isNotBlank(bo.getDeliveryId()), WmStoreOrder::getDeliveryId, bo.getDeliveryId());
        lqw.eq(bo.getGainIntegral() != null, WmStoreOrder::getGainIntegral, bo.getGainIntegral());
        lqw.eq(bo.getUseIntegral() != null, WmStoreOrder::getUseIntegral, bo.getUseIntegral());
        lqw.eq(bo.getPayIntegral() != null, WmStoreOrder::getPayIntegral, bo.getPayIntegral());
        lqw.eq(bo.getBackIntegral() != null, WmStoreOrder::getBackIntegral, bo.getBackIntegral());
        lqw.eq(StringUtils.isNotBlank(bo.getMark()), WmStoreOrder::getMark, bo.getMark());
        lqw.eq(bo.getIsDel() != null, WmStoreOrder::getIsDel, bo.getIsDel());
        lqw.eq(StringUtils.isNotBlank(bo.getUnique()), WmStoreOrder::getUnique, bo.getUnique());
        lqw.eq(bo.getMerId() != null, WmStoreOrder::getMerId, bo.getMerId());
        lqw.eq(bo.getIsMerCheck() != null, WmStoreOrder::getIsMerCheck, bo.getIsMerCheck());
        lqw.eq(bo.getCombinationId() != null, WmStoreOrder::getCombinationId, bo.getCombinationId());
        lqw.eq(bo.getPinkId() != null, WmStoreOrder::getPinkId, bo.getPinkId());
        lqw.eq(bo.getCost() != null, WmStoreOrder::getCost, bo.getCost());
        lqw.eq(bo.getSeckillId() != null, WmStoreOrder::getSeckillId, bo.getSeckillId());
        lqw.eq(bo.getBargainId() != null, WmStoreOrder::getBargainId, bo.getBargainId());
        lqw.eq(StringUtils.isNotBlank(bo.getVerifyCode()), WmStoreOrder::getVerifyCode, bo.getVerifyCode());
        lqw.eq(bo.getStoreId() != null, WmStoreOrder::getStoreId, bo.getStoreId());
        lqw.eq(bo.getShippingType() != null, WmStoreOrder::getShippingType, bo.getShippingType());
        lqw.eq(bo.getIsChannel() != null, WmStoreOrder::getIsChannel, bo.getIsChannel());
        lqw.eq(bo.getIsRemind() != null, WmStoreOrder::getIsRemind, bo.getIsRemind());
        lqw.eq(bo.getIsSystemDel() != null, WmStoreOrder::getIsSystemDel, bo.getIsSystemDel());


        return lqw;
    }


    /**
     * 新增订单
     */
    @Override
    public Boolean insertByBo(WmStoreOrderBo bo) {
        WmStoreOrder add = BeanUtil.toBean(bo, WmStoreOrder.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改订单
     */
    @Override
    public Boolean updateByBo(WmStoreOrderBo bo) {
        WmStoreOrderVo wmStoreOrderVo = baseMapper.selectVoById(bo.getId());
        if (ObjectUtil.isNull(wmStoreOrderVo)) {
            throw new ServiceException("订单不存在");
        }

        if (!OrderStatusEnum.STATUS_0.getValue().equals(wmStoreOrderVo.getStatus()) ||
            OrderStatusEnum.PAY_STATUS_0.getValue().equals(wmStoreOrderVo.getPaid())) {
            throw new ServiceException("订单状态错误");
        }

        if (!OrderStatusEnum.REFUND_STATUS_0.getValue().equals(wmStoreOrderVo.getRefundStatus())) {
            throw new ServiceException("订单退款中或已退款");
        }

//        YxExpress expressQueryVo = expressService.getOne(new LambdaQueryWrapper<YxExpress>().eq(YxExpress::getName, deliveryName));
//        if (ObjectUtil.isNull(expressQueryVo)) {
//            throw new YshopException("请后台先添加快递公司");
//        }

        //判断拼团产品
//        if (wmStoreOrderVo.getPinkId() != null && wmStoreOrderVo.getPinkId() > 0) {
//            YxStorePink pink = pinkService.getById(wmStoreOrderVo.getPinkId());
//            if (!OrderInfoEnum.PINK_STATUS_2.getValue().equals(pink.getStatus())) {
//                throw new YshopException("拼团未成功不能发货");
//            }
//        }

        WmStoreOrder update = BeanUtil.toBean(bo, WmStoreOrder.class);

        WmStoreOrder storeOrder = WmStoreOrder.builder()
            .id(wmStoreOrderVo.getId())
            .status(OrderStatusEnum.STATUS_1.getValue())
            .deliveryId(bo.getDeliveryId())
            .deliveryName(bo.getDeliveryName())
            .deliveryType(bo.getDeliveryType())
            .deliverySn(bo.getDeliverySn())
            .build();

        baseMapper.updateById(update);

        //增加状态
        WmStoreOrderRecordBo insertStoreOrderRecordBo = WmStoreOrderRecordBo.builder().oid(bo.getId())
            .changeType(OrderLogEnum.DELIVERY_GOODS.getValue())
            .changeMessage("已发货 快递公司：" + bo.getDeliveryName() + "快递单号：" + bo.getDeliveryId()).build();
        wmStoreOrderRecordService.insertByBo(insertStoreOrderRecordBo);

        //模板消息发布事件

        //加入redis，7天后自动确认收货
        String redisKey = String.valueOf(StrUtil.format("{}{}",
            ShopConstants.REDIS_ORDER_OUTTIME_UNCONFIRM, wmStoreOrderVo.getId()));
        redisTemplate.opsForValue().set(redisKey, wmStoreOrderVo.getOrderId(),
            ShopConstants.ORDER_OUTTIME_UNCONFIRM, TimeUnit.DAYS);
        return true;

    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(WmStoreOrder entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除订单
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public WmOrderNowOrderRecordVo getOrderStatus(Long id) {

        List<String> statusList = new ArrayList<>();
        statusList.add(OrderLogEnum.CREATE_ORDER.getValue());
        statusList.add(OrderLogEnum.PAY_ORDER_SUCCESS.getValue());
        statusList.add(OrderLogEnum.DELIVERY_GOODS.getValue());
        statusList.add(OrderLogEnum.TAKE_ORDER_DELIVERY.getValue());
        statusList.add(OrderLogEnum.EVAL_ORDER.getValue());
        List<WmStoreOrderRecordVo> orderStatusLogList = wmStoreOrderRecordService.queryListOrderByChangeTime(id, statusList);
        List<WmStoreOrderRecordVo> dtoList = getOrderStatusDto(orderStatusLogList);
        WmOrderNowOrderRecordVo wmOrderNowOrderRecordVo = new WmOrderNowOrderRecordVo();
        wmOrderNowOrderRecordVo.setSize(dtoList.size());
        dtoList.forEach(dto -> {
            if (OrderLogEnum.CREATE_ORDER.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setCacheKeyCreateOrder(dto.getChangeTime());
            }
            if (OrderLogEnum.PAY_ORDER_SUCCESS.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setPaySuccess(dto.getChangeTime());
            }
            if (OrderLogEnum.DELIVERY_GOODS.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setDeliveryGoods(dto.getChangeTime());
            }
            if (OrderLogEnum.TAKE_ORDER_DELIVERY.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setUserTakeDelivery(dto.getChangeTime());
                wmOrderNowOrderRecordVo.setOrderVerific(dto.getChangeTime());
            }
            if (OrderLogEnum.EVAL_ORDER.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setCheckOrderOver(dto.getChangeTime());
            }
        });

        statusList = new ArrayList<>();
        statusList.add(OrderLogEnum.REFUND_ORDER_APPLY.getValue());
        statusList.add(OrderLogEnum.REFUND_ORDER_SUCCESS.getValue());
        orderStatusLogList = wmStoreOrderRecordService.queryListOrderByChangeTime(id, statusList);
        dtoList = getOrderStatusDto(orderStatusLogList);
        dtoList.forEach(dto -> {
            if (OrderLogEnum.REFUND_ORDER_APPLY.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setApplyRefund(dto.getChangeTime());
            }
            if (OrderLogEnum.REFUND_ORDER_SUCCESS.getDesc().equals(dto.getChangeType())) {
                wmOrderNowOrderRecordVo.setRefundOrderSuccess(dto.getChangeTime());
            }

        });

        return wmOrderNowOrderRecordVo;
    }

    public List<WmStoreOrderRecordVo> getOrderStatusDto(List<WmStoreOrderRecordVo> orderStatusLogList) {
        return orderStatusLogList.stream().peek(log -> {
            log.setChangeType(OrderLogEnum.getDesc(log.getChangeType()));
            log.setChangeTime(log.getChangeTime());
        }).collect(Collectors.toList());
    }
}
